/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.web.jitengine;

import com.inspur.edp.lcm.metadata.api.entity.*;
import com.inspur.edp.web.common.JITEngineConstants;
import com.inspur.edp.web.common.constant.FrontendProjectConstant;
import com.inspur.edp.web.common.constant.WebCommonExceptionConstant;
import com.inspur.edp.web.common.customexception.WebCustomException;
import com.inspur.edp.web.common.entity.NodeJsCommandEnum;
import com.inspur.edp.web.common.entity.TerminalType;
import com.inspur.edp.web.common.environment.ExecuteEnvironment;
import com.inspur.edp.web.common.environment.checker.ExecuteEnvironmentChecker;
import com.inspur.edp.web.common.io.FileUtility;
import com.inspur.edp.web.common.io.NodeJsCommandResult;
import com.inspur.edp.web.common.io.NodejsFunctionUtility;
import com.inspur.edp.web.common.logger.WebLogger;
import com.inspur.edp.web.common.metadata.GspProjectUtility;
import com.inspur.edp.web.common.metadata.MetadataGetterParameter;
import com.inspur.edp.web.common.metadata.MetadataTypeEnum;
import com.inspur.edp.web.common.metadata.MetadataUtility;
import com.inspur.edp.web.common.utility.CommandLineUtility;
import com.inspur.edp.web.common.utility.OperatingSystemUtility;
import com.inspur.edp.web.common.utility.ResourceLocalizeUtil;
import com.inspur.edp.web.common.utility.StringUtility;
import com.inspur.edp.web.formmetadata.i18n.constant.I18nResourceConstant;
import com.inspur.edp.web.formmetadata.metadata.formdom.FormDOM;
import com.inspur.edp.web.formmetadata.metadataanalysis.*;
import com.inspur.edp.web.formmetadata.metadataanalysis.form.AnalysisExternalComponentResult;
import com.inspur.edp.web.formmetadata.metadataanalysis.form.FormComponentParser;
import com.inspur.edp.web.formmetadata.resolver.ResolveFormMetadataItem;
import com.inspur.edp.web.formmetadata.service.FormMetadataService;
import com.inspur.edp.web.frontendproject.entity.FrontendProjectGenerateParameter;
import com.inspur.edp.web.jitengine.babelgrnerate.GenerateForBabel;
import com.inspur.edp.web.jitengine.constant.I18nExceptionConstant;
import com.inspur.edp.web.jitengine.constant.I18nMsgConstant;
import com.inspur.edp.web.jitengine.dynamicform.htmltemplate.HtmlTemplateEntity;
import com.inspur.edp.web.jitengine.dynamicform.htmltemplate.HtmlTemplateManager;
import com.inspur.edp.web.jitengine.expressions.ExpressionFormGenerator;
import com.inspur.edp.web.jitengine.expressions.ExpressionManifest;
import com.inspur.edp.web.jitengine.expressions.ModuleFormExpressions;
import com.inspur.edp.web.jitengine.expressions.utility.ExpressionUtility;
import com.inspur.edp.web.jitengine.i18nresource.GenerateResourceManager;
import com.inspur.edp.web.jitengine.i18nresource.GeneratedI18nResourceList;
import com.inspur.edp.web.jitruntimebuild.api.entity.JitBuildParameter;
import com.inspur.edp.web.npmpackage.api.entity.CheckNpmPackageJsonParam;
import com.inspur.edp.web.npmpackage.api.entity.NpmInstallParameter;
import com.inspur.edp.web.npmpackage.api.entity.NpmPackageResponse;
import com.inspur.edp.web.npmpackage.core.npminstall.NodeModulesPathGenerator;
import com.inspur.edp.web.npmpackage.core.npminstall.NpmInstallManager;
import com.inspur.edp.web.npmpackage.core.npminstall.PackageJsonPathGenerator;
import com.inspur.edp.web.npmpackage.core.npmsetting.NpmSettingManager;
import org.apache.commons.lang3.SystemUtils;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;

/**
 * JIT Engine 管理
 *
 * @author noah
 */
public class JITEngineManager {


    public static void generateFrontendProjectForBabel(String currentProjectPath) {
        String devRootPath = MetadataUtility.getInstance().getDevRootPath();
        if (!FileUtility.isAbsolute(devRootPath)) {
            devRootPath = FileUtility.getAbsolutePathHead(devRootPath) + devRootPath;
        }

        GenerateForBabel.generateFrontendProjectForBabel(currentProjectPath, devRootPath);
    }

    /**
     * @param currentBuildPath
     */
    public static String buildFrontendProjectWithRuntime(String currentBuildPath, boolean isUpgradeTool, JitBuildParameter buildParameter) {

        String commandArgs = "";
        boolean useBabel = getRuntimeWebBuildNoah(isUpgradeTool);
        if (useBabel) {
            String runtimeBuildRootPath = getRuntimeBuildRoot(isUpgradeTool);
            String buildAppjs = "@farris/farris-babel/app.js";

            // 获取对应的nodejs命令
            String nodeCommand = NodejsFunctionUtility.getNodeJsCommandInServerWithOS(NodeJsCommandEnum.Node, isUpgradeTool);

            String nodeBuild = nodeCommand + buildAppjs + "  --paths=" + runtimeBuildRootPath + " --workspacepath=" + currentBuildPath;
            if (!OperatingSystemUtility.isLinux()) {
                commandArgs += "cd " + runtimeBuildRootPath;
                commandArgs += " && " + nodeBuild;
                commandArgs += " && exit";
            } else {
                commandArgs += "cd " + runtimeBuildRootPath;
                commandArgs += " &&  " + nodeBuild;
            }
        } else {
            // npm在线安装执行
            try {
                NpmPackageResponse packageResponse = NpmInstallManager.npmInstallWithDefault(isUpgradeTool, true, false);
                if (!packageResponse.isSuccess()) {
                    WebLogger.Instance.info(packageResponse.getErrorMessage(), JITEngineManager.class.getName());
                }
            } catch (Exception ex) {
                WebLogger.Instance.info(ex.getMessage() + Arrays.toString(ex.getStackTrace()), JITEngineManager.class.getName());
            }

            //判断是否存在node_modules 文件夹  避免由于忘放置而导致得问题
            //if (!buildParameter.isMobileApp() && !buildParameter.isMobileApprove() && !buildParameter.isBabelCompile() &&
            // 针对升级工具 不执行在线安装
            if (!buildParameter.isMobileApprove() && !buildParameter.isBabelCompile() &&
                    !buildParameter.isInUpgradeTool() && !FileUtility.exists(buildParameter.getBuildNodeModulePath())) {
                String errorMessage = ResourceLocalizeUtil.getString(I18nMsgConstant.WEB_JIT_ENGINE_MSG_0001, buildParameter.getBuildNodeModulePath());
                WebLogger.Instance.error(errorMessage, JITEngineManager.class.getName());
                return errorMessage;
            }

            /// 不考虑npm使用安装盘的情况
            ExecuteEnvironmentChecker.beforeCompile();

            // 编译前创建nodeModules软链接
            if (buildParameter.isMobileApp()) {
                createNodeModulesLink(currentBuildPath,true);
            }

            if (!OperatingSystemUtility.isLinux()) {
                commandArgs += "cd " + currentBuildPath;
                commandArgs += " && " + "npm run build ";
                commandArgs += " && exit";
            } else {
                commandArgs += "cd " + currentBuildPath;
                commandArgs += " && " + "npm run build";
                commandArgs += "  && exit ";
            }
        }

        try {
            return CommandLineUtility.runCommand(commandArgs);
        } catch (WebCustomException e) {
            String errorMsg = "";
            if (!useBabel) {
                CheckNpmPackageJsonParam checkResult = NpmInstallManager.checkNpmPackageJson(ExecuteEnvironment.Runtime);
                if (checkResult.getSuccessCode() > 0) {
                    errorMsg += checkResult.getMessage() + "<br>";
                }
                errorMsg += ResourceLocalizeUtil.getString(I18nMsgConstant.WEB_JIT_ENGINE_MSG_0004, "npm run build", e.getMessage());;
            }
            throw new WebCustomException(errorMsg, e);
        }
    }


    /**
     * 生成前端工程，目前仅支持生成 Angular 工程
     */
    public static void generateFrontendProject(FrontendProjectGenerateParameter generateParameter, String refNodeModulesBasePath, TerminalType terminalType) {
        if (StringUtility.isNullOrEmpty(generateParameter.getProjectPath())) {
            return;
        }

        boolean isJieXiForm = generateParameter.isForceUseJieXi();

        String currentProjectPath = generateParameter.getProjectPath();
        String currentProjectName = GspProjectUtility.getProjectName(currentProjectPath);

        GspProject gspProject = GspProjectUtility.getProjectInformation(currentProjectPath);
        if (!FileUtility.isAbsolute(currentProjectPath)) {
            // 开发根路径
            String devRootPath = MetadataUtility.getInstance().getDevRootPath();
            if (!currentProjectPath.startsWith(devRootPath)) {
                currentProjectPath = FileUtility.combine(devRootPath, currentProjectPath);
            }
            if (!FileUtility.isAbsolute(currentProjectPath)) {
                currentProjectPath = FileUtility.getAbsolutePathHead(currentProjectPath) + currentProjectPath;
            }
        }

        WebLogger.Instance.info("编译工程路径为:" + currentProjectPath, JITEngineManager.class.getName());
        String serviceUnitPath = gspProject.getSuDeploymentPath();

        String formType = terminalType.toString().toLowerCase();
        String frameworkType = terminalType.getFrameworkType();

        Path path = Paths.get(currentProjectPath);
        String formJsonAbsolutePath = path.resolve(getProjectFormRelativeResolvePath(terminalType, isJieXiForm)).toString();
        String formPublishAbsolutePath = path.resolve(getProjectFormRelativeGeneratePath(terminalType, isJieXiForm)).toString();
        formPublishAbsolutePath = formPublishAbsolutePath.replace("\\", "/");
        ProjectCompileContext projectCompileContext = new ProjectCompileContext(currentProjectName,
                getRefNodeModulesPath(refNodeModulesBasePath, terminalType),
                formType, frameworkType,
                formJsonAbsolutePath,
                formPublishAbsolutePath, path.resolve("eapi").toString(), serviceUnitPath, ExecuteEnvironment.Design);

        // 设置是否解析表单
        projectCompileContext.setJieXiForm(isJieXiForm);
        compileProject(projectCompileContext);
    }

    public static String getRefNodeModulesPath(String refNodeModulesBasePath, TerminalType terminalType) {
        boolean enableMobileBuildWithNpmInstalled = NpmSettingManager.getNpmSetting(false).getSettingConfig().isEnableMobileBuild();
        refNodeModulesBasePath = refNodeModulesBasePath.replace("\\", "/");

        String refNodeModulesPath = "";

        Path path = Paths.get(refNodeModulesBasePath);
        switch (terminalType) {
            case PC:
                refNodeModulesPath = path.resolve("node_modules").toString();
                break;
            case MOBILE:
                if (enableMobileBuildWithNpmInstalled) {
                    refNodeModulesPath = path.resolve("node_modules").toString();
                } else {
                    refNodeModulesPath = FileUtility.combine(refNodeModulesBasePath, "node_modules");
                }
                break;
            default:
                throw new WebCustomException(I18nExceptionConstant.WEB_JIT_ENGINE_ERROR_0001, new String[]{String.valueOf(terminalType)});
        }

        return refNodeModulesPath;
    }

    /**
     * 获取工程中表单的相对解析路径
     * TODO：迁移到公共方法
     *
     * @param terminalType
     * @return
     */
    public static String getProjectFormRelativeResolvePath(TerminalType terminalType, boolean isJieXiForm) {
        String projectFormRelativeResolvePath = "";
        Path path = Paths.get(FrontendProjectConstant.FRONTEND_PROJECT_COMPILE_PATH);
        switch (terminalType) {
            case PC:
                if (isJieXiForm) {
                    projectFormRelativeResolvePath = path.resolve(FrontendProjectConstant.PROJECT_GENERATE_PATH_FOR_Dynamic).toString();
                } else {
                    projectFormRelativeResolvePath = path.resolve(TerminalType.PC.getWebDevPathName()).toString();
                }
                break;
            case MOBILE:
                projectFormRelativeResolvePath = path.resolve(TerminalType.MOBILE.getWebDevPathName()).toString();
                break;
            default:
                throw new WebCustomException(WebCommonExceptionConstant.WEB_COMMON_ERROR_0010, new String[]{String.valueOf(terminalType)});
        }

        return projectFormRelativeResolvePath;
    }

    /**
     * 获取工程中表单的相对生成路径
     * TODO：迁移到公共方法
     *
     * @param terminalType
     * @return
     */
    private static String getProjectFormRelativeGeneratePath(TerminalType terminalType, boolean isJieXiForm) {
        String projectFormRelativeGeneratePath = "";
        Path path = Paths.get(FrontendProjectConstant.FRONTEND_PROJECT_COMPILE_PATH);
        switch (terminalType) {
            case PC:
                if (isJieXiForm) {
                    projectFormRelativeGeneratePath = path.resolve(FrontendProjectConstant.PROJECT_GENERATE_PATH_FOR_Dynamic).toString();
                } else {
                    projectFormRelativeGeneratePath = path.resolve(TerminalType.PC.getAppPathName()).toString();
                }
                break;
            case MOBILE:
                projectFormRelativeGeneratePath = path.resolve(TerminalType.MOBILE.getAppPathName()).toString();
                break;
            default:
                throw new WebCustomException(WebCommonExceptionConstant.WEB_COMMON_ERROR_0010, new String[]{String.valueOf(terminalType)});
        }

        return projectFormRelativeGeneratePath;
    }

    /**
     * 解析表单元数据
     *
     * @return
     */
    public static void ResolveFormMetadata(ResolveFormMetadataItem resolveFormMetadataItem, String baseFormPath, String projectPath, String targetStorageBasePath, String formServiceBasePath, HashMap<String, CommandsAnalysis.WebComponentMetadataAndExtra> projectCmpList,
                                           GeneratedI18nResourceList i18nResourceList, GeneratedI18nResourceList zhI18nResourceList, GeneratedI18nResourceList zhCHTI18nResourceList,
                                           ExpressionManifest expressionManifest, String i18nResourceKeyPrefix, ExecuteEnvironment executeEnvironment, boolean isUpdradeTool) {
        Map<String, GeneratedI18nResourceList> i18nResourceListMap = new HashMap<>();
        i18nResourceListMap.put(I18nResourceConstant.En, i18nResourceList);
        i18nResourceListMap.put(I18nResourceConstant.ZH_CHS, zhI18nResourceList);
        i18nResourceListMap.put(I18nResourceConstant.ZH_CHT, zhCHTI18nResourceList);
        resolveFormMetadata(resolveFormMetadataItem, baseFormPath, projectPath, targetStorageBasePath, formServiceBasePath, projectCmpList, i18nResourceListMap, expressionManifest, i18nResourceKeyPrefix, executeEnvironment, isUpdradeTool);
    }

    /**
     * 解析表单元数据
     * 遍历语言列表中的全部语言
     * @return
     */
    public static void resolveFormMetadata(ResolveFormMetadataItem resolveFormMetadataItem, String baseFormPath, String projectPath, String targetStorageBasePath, String formServiceBasePath, HashMap<String, CommandsAnalysis.WebComponentMetadataAndExtra> projectCmpList,
                                           Map<String , GeneratedI18nResourceList> i18nResourceListMap,
                                           ExpressionManifest expressionManifest, String i18nResourceKeyPrefix, ExecuteEnvironment executeEnvironment, boolean isUpdradeTool) {
        FormAnalysis formAnalysis = new FormAnalysis(ExecuteEnvironment.Design, false);

        // Resolve Form
        FormAnalysis.ResolveFormResult resolveFormResult = formAnalysis.resolveForm(resolveFormMetadataItem.getGspMetadata().getHeader().getFileName(), resolveFormMetadataItem.getGspMetadata().getRelativePath(), targetStorageBasePath, projectPath);

        FormDOM json = resolveFormResult.getJson();
        String strRelativePath = resolveFormResult.getRelativePath();

        // 设置表单元数据的是否解析标识
        resolveFormMetadataItem.setFormDynamicTag(json.getOptions().isEnableFormJieXi());

        // 设置表达式 manifest.json 文件的code及其对应的文件名
        expressionManifest.setFormModuleCode(json.getModule().getCode());
        expressionManifest.setManifestJsonPath(ExpressionUtility.getExpressionManifestJsonPath(json.getModule().getCode()));

        // 如果是单个表单解析预览  那么保存表单元数据内容
        if (resolveFormMetadataItem.getCalculateIsDynamicForm()) {
            // 屏蔽metadata.js 的保存
            // DynamicFormModuleOperation.saveForMetadataJs(json, targetStorageBasePath, json.getModule().getCode());
            // 写入html模板文件
            String htmlTemplateRelativePath = TerminalType.PC.getHtmlTemplateRelativePath(true);
            String htmlTemplatePath = FileUtility.combine(projectPath, htmlTemplateRelativePath, json.getModule().getCode().toLowerCase());

            try {
                // 首先移除 该目录下out-tsc及dist-rollup
                FileUtility.deleteFolder(FileUtility.combine(htmlTemplatePath, "out-tsc"));
                FileUtility.deleteFolder(FileUtility.combine(htmlTemplatePath, JITEngineConstants.DistRollupPathName));
            } catch (Exception ex) {
                WebLogger.Instance.info("delete html template folder failed, the path is " + htmlTemplatePath + ", the trace is " + ex.getMessage() + Arrays.toString(ex.getStackTrace()));
            }

            List<HtmlTemplateEntity> htmlTemplateEntityList = HtmlTemplateManager.generate(htmlTemplatePath, json);

            // 如果包含模板文件  那么执行编译
            NodeJsCommandResult tscCommandResult = NodejsFunctionUtility.getTscCommandInServer(false);
            String args = "cd " + htmlTemplatePath + " && npm run build ";

            String tscBuildResult = CommandLineUtility.runCommand(args);
            if (!StringUtility.isNullOrEmpty(tscBuildResult)) {
                WebLogger.Instance.info(tscBuildResult, CommandServiceAnalysis.class.getName());
            }
//
//            NodeJsCommandResult rollupCommandResult = NodejsFunctionUtility.getRollupCommandInServer(false);
//            String rollupPath = FileUtility.combine(htmlTemplatePath, "dist-tsc");
            // 对每个js文件进行打包

//                String fileNameWithoutExtension = FilenameUtils.getBaseName(item.get);
//            String rollupCommand = "cd " + rollupPath + " &&  " + rollupCommandResult.getNodeJsCommand();
//            rollupCommand += "  -f system   ";
//            rollupCommand += "-i  app.module.js  ";
//            rollupCommand += "-o ../dist-rollup/webapp-templates.module.ngfactory.js";
//            String rollupBuildResult = CommandLineUtility.runCommand(rollupCommand);
//            if (!StringUtility.isNullOrEmpty(rollupBuildResult)) {
//                WebLogger.Instance.info(rollupBuildResult, CommandServiceAnalysis.class.getName());
//            }

            String sourceBuildPath = FileUtility.combine(htmlTemplatePath, JITEngineConstants.DistRollupPathName, "web-page-templates");
            if (FileUtility.exists(sourceBuildPath)) {
                String htmlTemplateFileDeployToFormFolder = FileUtility.combine(targetStorageBasePath, "htmltemplate");
                if (FileUtility.exists(htmlTemplateFileDeployToFormFolder)) {
                    FileUtility.forceDelete(htmlTemplateFileDeployToFormFolder);
                }
                // 将dist-rollup 拷贝
                FileUtility.copyFolder(sourceBuildPath, htmlTemplateFileDeployToFormFolder);
            }
        }


        // 拷贝package.json 文件到当前目录
        if (!resolveFormMetadataItem.getCalculateIsDynamicForm()) {
            copyPackageJson(targetStorageBasePath, isUpdradeTool);
        }
        ResolveFormMetadataWithVisualDom(resolveFormMetadataItem, baseFormPath, json, targetStorageBasePath, strRelativePath, formServiceBasePath, projectPath, projectCmpList, targetStorageBasePath,
                i18nResourceListMap, expressionManifest, null, i18nResourceKeyPrefix, executeEnvironment, isUpdradeTool, null);
    }

    private static void copyPackageJson(String webDevPath, boolean isUpdradeTool) {
        String packageJsonInServerPath = PackageJsonPathGenerator.generate(isUpdradeTool, false);
        File packageJsonFile = new File(packageJsonInServerPath);
        if (packageJsonFile.exists()) {
            // 拷贝至目标文件目录
            String projectWebDevPackageJsonPath = FileUtility.combine(webDevPath, "package.json");
            FileUtility.copyFile(packageJsonInServerPath, projectWebDevPackageJsonPath, true);
        }
    }

    private static void ResolveFormMetadataWithVisualDom(ResolveFormMetadataItem resolveFormMetadataItem, String baseFormPath, FormDOM formDom, String targetStorageBasePath, String relativePath,
                                                         String formServiceBasePath, String projectPath, HashMap<String, CommandsAnalysis.WebComponentMetadataAndExtra> projectCmpList, String webDevPath,
                                                         // GeneratedI18nResourceList i18nResourceList, GeneratedI18nResourceList zhI18nResourceList, GeneratedI18nResourceList zhCHTI18nResourceList,
                                                         Map<String, GeneratedI18nResourceList> i18nResourceListMap,
                                                         ExpressionManifest expressionManifest,
                                                         String containerId,
                                                         String i18nResourceKeyPrefix,
                                                         ExecuteEnvironment executeEnvironment, boolean isUpdradeTool, HashMap<String, Object> externalComponentItem) {
        String formMetadataName = resolveFormMetadataItem.getGspMetadata().getHeader().getFileName();


        // 解析表单时  只解析表单元数据和对应命令元数据  不再解析其他类型元数据
        if (!resolveFormMetadataItem.getCalculateIsDynamicForm()) {
            // Resolve Form Internal Route
            FormComponentParser.getInstance().ExtractComponent(formDom, formMetadataName, targetStorageBasePath, webDevPath);

            // Resolve StateMachine
            StateMachineAnalysis stateMachineAnalysis=new StateMachineAnalysis(ExecuteEnvironment.Design,false);
            stateMachineAnalysis.resolveStateMachine(formDom, formMetadataName, targetStorageBasePath, baseFormPath);
        }

        // Resolve Command
        // 增加是否解析表单参数 如果为解析表单 那么不再生成对应的command.json 文件
        CommandsAnalysis commandsAnalysis = new CommandsAnalysis(ExecuteEnvironment.Design, false, !resolveFormMetadataItem.getCalculateIsDynamicForm());
        commandsAnalysis.resolveCommand(CommandsAnalysis.ResolveCommandParameter.init(resolveFormMetadataItem.getGspMetadata(), formDom, baseFormPath),
                formMetadataName, targetStorageBasePath, projectCmpList);

        if (!resolveFormMetadataItem.getCalculateIsDynamicForm()) {
            // Resolve Eapi
            EapiAnalysis eapiAnalysis = new EapiAnalysis(ExecuteEnvironment.Design, false);
            String formNameSpace = resolveFormMetadataItem.getGspMetadata() == null ? "" : resolveFormMetadataItem.getGspMetadata().getHeader().getNameSpace();
            eapiAnalysis.resolveEapi(formDom, formMetadataName, targetStorageBasePath, formNameSpace, relativePath);
        }

        // Resolve Command Service
        CommandServiceAnalysis.resolveCommandService(formDom, relativePath, formServiceBasePath, targetStorageBasePath, resolveFormMetadataItem);

        // // 构造对应的国际化资源项
        // GenerateResourceManager.generateI18nResource(resolveFormMetadataItem.getGspMetadata(), baseFormPath, i18nResourceList, relativePath, i18nResourceKeyPrefix, I18nResourceConstant.En);
        //
        // // 构建繁体中文
        // GenerateResourceManager.generateI18nResource(resolveFormMetadataItem.getGspMetadata(), baseFormPath, zhCHTI18nResourceList, relativePath, i18nResourceKeyPrefix, I18nResourceConstant.ZH_CHT);
        // 构建除简体中文外的其他语言
        GeneratedI18nResourceList zhI18nResourceList = null;
        for (Map.Entry<String, GeneratedI18nResourceList> entry : i18nResourceListMap.entrySet()) {
            String key = entry.getKey();
            GeneratedI18nResourceList value = entry.getValue();
            if (I18nResourceConstant.ZH_CHS.equals(key)) {
                zhI18nResourceList = value;
                continue;
            }
            GenerateResourceManager.generateI18nResource(resolveFormMetadataItem.getGspMetadata(), baseFormPath, value, relativePath, i18nResourceKeyPrefix, key);
        }


        ///获取表单对应的表达式
        ModuleFormExpressions moduleFormExpressions = ExpressionFormGenerator.generate(formDom, containerId, externalComponentItem);
        // 仅仅在包含表达式时才进行添加
        if (moduleFormExpressions != null && moduleFormExpressions.getExpressions() != null && moduleFormExpressions.getExpressions().size() > 0) {
            expressionManifest.getExpressions().add(moduleFormExpressions);
        }

        if (!resolveFormMetadataItem.getCalculateIsDynamicForm()) {
            // 检查zhI18nResourceList不为空
            Objects.requireNonNull(zhI18nResourceList);
            // 解析组合表单
            resolveExternalDependencyMetadata(resolveFormMetadataItem.getGspMetadata(), baseFormPath, formDom, targetStorageBasePath, zhI18nResourceList, relativePath, i18nResourceKeyPrefix);

            // 递归解析   如果存在子组件 那么需要递归执行子组件
            executeExternalComponent(formDom, baseFormPath, targetStorageBasePath, formServiceBasePath, projectPath, projectCmpList, webDevPath,
                    i18nResourceListMap, relativePath, expressionManifest, i18nResourceKeyPrefix, executeEnvironment, isUpdradeTool);
        }
    }

    /**
     * 处理扩展组件
     */
    private static void executeExternalComponent(FormDOM json, String baseFormPath, String targetStorageBasePath, String formServicePath, String projectPath,
                                                 HashMap<String, CommandsAnalysis.WebComponentMetadataAndExtra> projectCmpList, String webdevpath,
                                                 // GeneratedI18nResourceList i18nResourceList, GeneratedI18nResourceList zhI18nResourceList, GeneratedI18nResourceList zhCHTI18nResourceList,
                                                 Map<String, GeneratedI18nResourceList> i18nResourceListMap,
                                                 String relativePath,
                                                 ExpressionManifest expressionManifest,
                                                 String i18nResourceKeyPrefix, ExecuteEnvironment executeEnvironment, boolean isUpdradeTool) {
        if (json != null && json.getModule() != null && json.getModule().getExternalComponents() != null && json.getModule().getExternalComponents().size() > 0) {
            for (HashMap<String, Object> item : json.getModule().getExternalComponents()) {
                String strI18nResourcePrefix = i18nResourceKeyPrefix;
                FormAnalysis formAnalysis = new FormAnalysis(executeEnvironment, isUpdradeTool);

                AnalysisExternalComponentResult externalVisualDom;

                // 判断是否是弹窗Url形式
                if (item.containsKey("contentType") && StringUtility.getOrDefault(item.get("type"), "").equals("ModalContainer") && StringUtility.getOrDefault(item.get("contentType"), "").equals("url")) {
                    externalVisualDom = null;
                    // 如果是弹窗URL 那么不解析对应元数据
                } else {
                    externalVisualDom = formAnalysis.analysisExternalComponent(targetStorageBasePath, json.getModule().getCode(), relativePath, item, webdevpath);
                }


                // 如果获取不到对应的表单元数据。针对的是弹窗Url  情况
                if (externalVisualDom != null && externalVisualDom.getJson() != null) {
                    // 如果是独立加载表单  那么不再继续执行后续的组合表单文件生成
                    if (externalVisualDom.isUseIsolateJs()) {
                        return;
                    }

                    String externalFormBasePath = FileUtility.combine(targetStorageBasePath, json.getModule().getCode().toLowerCase(), externalVisualDom.getExternalComponentPath());
                    /// 表单service文件路径
                    String externalFormServicePath = FileUtility.combine(formServicePath, json.getModule().getCode().toLowerCase(), "externalcomponents", externalVisualDom.getExternalComponentPath());
                    GspMetadata formMetadata = MetadataUtility.getInstance().getMetadataWithEnvironment(() -> {
                        MetadataGetterParameter.GetterMetadataInfo getterMetadataInfo = new MetadataGetterParameter.GetterMetadataInfo();
                        getterMetadataInfo.setId(externalVisualDom.getExternalComponentUri());
                        getterMetadataInfo.setMetadataType(MetadataTypeEnum.Frm);
                        return getterMetadataInfo;
                    }, () -> {
                        MetadataGetterParameter.GetterMetadataInfo getterMetadataInfo = new MetadataGetterParameter.GetterMetadataInfo();
                        getterMetadataInfo.setMetadataType(MetadataTypeEnum.Frm);
                        getterMetadataInfo.setCode(json.getModule().getCode());
                        getterMetadataInfo.setName(json.getModule().getName());
                        return getterMetadataInfo;
                    }, executeEnvironment, isUpdradeTool);

                    String[] params = {externalVisualDom.getExternalComponentPath(), externalVisualDom.getJson().getModule().getCode()};
                    strI18nResourcePrefix = GenerateResourceManager.generateKeyPrefix(strI18nResourcePrefix, params).toLowerCase();

                    String strContainerId = getExternalContainerId(item);

                    ResolveFormMetadataItem resolveFormMetadataItem = new ResolveFormMetadataItem();
                    resolveFormMetadataItem.setGspMetadata(formMetadata);
                    ResolveFormMetadataWithVisualDom(resolveFormMetadataItem, baseFormPath, externalVisualDom.getJson(),
                            externalFormBasePath,
                            externalVisualDom.getRelativePath(), externalFormServicePath, projectPath,
                            projectCmpList, webdevpath, i18nResourceListMap, expressionManifest, strContainerId, strI18nResourcePrefix, executeEnvironment, isUpdradeTool,
                            item);

                }
            }
        }
    }

    private static String getExternalContainerId(HashMap<String, Object> item) {
        String externalComponentId = item.get("id").toString();
        String externalComponentType = item.get("type").toString();
        String containerId = item.get("containerId").toString();
        String strContainerId;
        if ("ModalContainer".equals(externalComponentType)) {
            strContainerId = !StringUtility.isNullOrEmpty(containerId) ? containerId : externalComponentId;
        } else {
            strContainerId = externalComponentId;
        }

        return strContainerId;
    }


    /**
     * 解析表单元数据依赖的外部元数据，如资源元数据
     */
    private static void resolveExternalDependencyMetadata(GspMetadata formMetadata, String baseFormPath, FormDOM json, String targetStorageBasePath, GeneratedI18nResourceList zhResourceList, String baseRelativePath, String i18nResourcePrefix) {

        List<I18nResource> resourceList = FormMetadataService.getBeanInstance().getFormResourceWithMetaData(formMetadata, I18nResourceConstant.ZH_CHS, baseFormPath);
        if (resourceList != null && resourceList.size() > 0) {
            resourceList.forEach((item) ->
            {
                I18nResourceItemCollection resourceItemCollection = item.getStringResources();
                if (resourceItemCollection == null || resourceItemCollection.size() == 0) {
                    return;
                }

                // 根据资源项构建生成结构
                StringBuilder resouceItemsStringBuilder = new StringBuilder();
                resouceItemsStringBuilder.append("{");
                for (int i = 0; i < resourceItemCollection.size(); i++) {
                    I18nResourceItem resourceItem = resourceItemCollection.get(i);
                    StringBuilder resouceItemStringBuilder = new StringBuilder();
                    String id = resourceItem.getKey();

                    String updatedId = id.substring(id.lastIndexOf(".") + 1); // 解析ID
                    String value = resourceItem.getValue();
                    String comment = resourceItem.getComment();
                    resouceItemStringBuilder.append("\"").append(updatedId).append("\": {").append("\r\n");
                    resouceItemStringBuilder.append("    \"name\": " + "\"").append(value).append("\",").append("\r\n");
                    for (String s : Arrays.asList("    \"comment\": " + "\"" + comment + "\"" + "\r\n", "}")) {
                        resouceItemStringBuilder.append(s);
                    }

                    zhResourceList.addIfNotExistsWithKey(GenerateResourceManager.generateResourceId(updatedId, i18nResourcePrefix), value);

                    if (i != resourceItemCollection.size() - 1) {
                        resouceItemStringBuilder.append(",");
                    }
                    resouceItemStringBuilder.append("\r\n");

                    resouceItemsStringBuilder.append(resouceItemStringBuilder);
                }
                resouceItemsStringBuilder.append("}");

                // 将生成的结构持久化到磁盘
                FileUtility.writeFile(targetStorageBasePath, String.format("%1$s%2$s", formMetadata.getHeader().getFileName().toLowerCase(), JITEngineConstants.ResourceJsonFile), resouceItemsStringBuilder.toString());
            });

        }
    }

    public static void compileProject(ProjectCompileContext projectCompileContext) {
        // 编译前执行install  install的是全局离线包
        NpmInstallBeforeGenerate.beforeCompileExecuteWithNpmInstall(projectCompileContext, projectCompileContext.executeEnvironment.equals(ExecuteEnvironment.Runtime), true);

        // 暂时不考虑jit-engine 放置于安装盘的情况 采用外部的
        ExecuteEnvironmentChecker.beforeGenerate();

        // 获取jit命令执行参数
        String jitCommand = NodejsFunctionUtility.getJitCommandInServer(projectCompileContext.executeEnvironment.equals(ExecuteEnvironment.UpgradeTool));
        String args = jitCommand;// "jit";
        args += " --projectName=" + projectCompileContext.getProjectName();
        args += " --projectRoute=" + projectCompileContext.getProjectRoute();
        args += " --linkedNodeModules=" + projectCompileContext.getNodeModulesAbsolutePath();
        args += " --formType=" + projectCompileContext.getFormType();
        args += " --frameworkType=" + projectCompileContext.getFrameworkType();
        args += " --mp=" + projectCompileContext.getFormJsonAbsolutePath();
        args += " --pp=" + projectCompileContext.getFormPublishAbsolutePath();
        args += " --eapiPath=" + projectCompileContext.getEapiAbsolutePath();
        args += " --uilib=" + projectCompileContext.getUserInterfaceLibrary();
        args += " --ngVersion=" + projectCompileContext.getAngularVersion();
        args += " --isEnableDynamicForm=true ";

        // 运行时定制当前不使用资源文件内容 增加参数控制 切换至res时，需要将该参数去除
        if (projectCompileContext.executeEnvironment.equals(ExecuteEnvironment.Runtime)) {
            if (!StringUtility.isNullOrEmpty(projectCompileContext.extraFormPath)) {
                // 如果维度 信息不为空，那么增加维度信息传参
                args += " --runTimeDimPath=" + projectCompileContext.extraFormPath;
            }
            args += " --isRuntime=true ";
            if (projectCompileContext.isGenerateViewModel()) {
                args += " --isGenerateViewModel=true";
            }
        }

        args += " --serviceUnitPath=" + projectCompileContext.getServiceUnitPath();
        if (projectCompileContext.getIsMobileApprove()) {
            args += " --isMobileApprove=" + projectCompileContext.getIsMobileApprove();
            args += " --mobileApproveFormCode=" + projectCompileContext.getMobileApproveFormCode();
        }

        if (projectCompileContext.isDeleteSourceCodeBeforeGenerate()) {
            args += " --deleteSourceCodeBeforeGenerate=" + projectCompileContext.isDeleteSourceCodeBeforeGenerate();
        }

        try {
            CommandLineUtility.runCommand(args);
        } catch (WebCustomException e) {
            String errorMsg = ResourceLocalizeUtil.getString(I18nMsgConstant.WEB_JIT_ENGINE_MSG_0004, "jit", e.getMessage());
            throw new WebCustomException(errorMsg, e);
        }

    }


    public static void buildFrontendProject(String targetProjectPath, ExecuteEnvironment executeEnvironment, TerminalType terminalType) {
        // 标识运行环境 是为运行时定制 或设计时
        boolean isRuntime = executeEnvironment.equals(ExecuteEnvironment.Runtime);
        NpmInstallBeforeGenerate.beforeCompileExecuteWithNpmInstall(executeEnvironment, isRuntime, false);

        // 检测node_modules 是否进行了部署
        checkNodeModuleDeployed(isRuntime);

        // 转换路径
        targetProjectPath = FileUtility.getPlatformIndependentPath(targetProjectPath);


        String currentBuildPath = getProjectBuildPath(targetProjectPath, terminalType);

        // 编译前创建nodeModules软链接
        if (terminalType == TerminalType.MOBILE) {
            createNodeModulesLink(currentBuildPath,isRuntime);
        }

        WebLogger.Instance.info("Build Path is:" + currentBuildPath, JITEngineManager.class.getName());

        /// 不考虑npm使用安装盘的情况
        ExecuteEnvironmentChecker.beforeCompile();

        String[] command = new String[1];
        if (SystemUtils.IS_OS_WINDOWS) {
            command = new String[4];
            // 盘符独立打开
            command[0] = FileUtility.getAbsolutePathHead(targetProjectPath);
            command[1] = "cd " + currentBuildPath;
            command[2] = "npm run build";
            command[3] = "exit";
        } else if (SystemUtils.IS_OS_LINUX || SystemUtils.IS_OS_MAC_OSX) {
            command = new String[3];
            command[0] = "cd " + currentBuildPath;
            command[1] = "npm run build";
            command[2] = "exit";
        }

        // 如果目标路径不存在 那么无需执行编译
        if (!FileUtility.exists(currentBuildPath)) {
            WebLogger.Instance.info("build path is not exists,so engine will not be to build this path,the path is " + currentBuildPath, JITEngineManager.class.getName());
            return;
        }

        if (terminalType == TerminalType.MOBILE) {
            checkHasMobileRelyNodeModule(currentBuildPath);
            // 针对移动工程编译   因为涉及到移动和PC node_modules的合并，如果移动的node_modules 未合并过来，那么进行友好的提示
            // 当前采取策略，读取当前软链接中的node_modules 判断里面是否包含移动固有的包
        }

        try {
            CommandLineUtility.runCommand(command);
        } catch (WebCustomException e) {
            CheckNpmPackageJsonParam checkResult = NpmInstallManager.checkNpmPackageJson(executeEnvironment);
            String errorMsg = "";
            if (checkResult.getSuccessCode() > 0) {
                errorMsg += checkResult.getMessage() + "<br>";
            }
            errorMsg += ResourceLocalizeUtil.getString(I18nMsgConstant.WEB_JIT_ENGINE_MSG_0004, "npm run build", e.getMessage());;
            throw new WebCustomException(errorMsg, e);
        }

    }

    /**
     * 创建移动nodeModules 软链接
     * @param currentBuildPath
     * @param isRuntime
     */
    private static void createNodeModulesLink(String currentBuildPath,boolean isRuntime){
        String nodeModulesLinkPath = FileUtility.combine(currentBuildPath, "node_modules");
        File nodeModulesLinkFile = new File(nodeModulesLinkPath);
        // 存在node_modules软链接
        if (nodeModulesLinkFile.exists()) {
            return;
        }
        NpmInstallParameter npmInstallParameter = new NpmInstallParameter();
        npmInstallParameter.setExecuteEnvironment(isRuntime ? ExecuteEnvironment.Runtime : ExecuteEnvironment.Design);
        String currentWorkSpace = FileUtility.getCurrentWorkPath();
        String parentNodeModulesPath = NodeModulesPathGenerator.generatePackageJsonPath(npmInstallParameter, currentWorkSpace);

        String nodeModulesPath = FileUtility.combine(parentNodeModulesPath, "node_modules");
        // 不存在 node_modules
        if (!FileUtility.exists(nodeModulesPath)) {
            return;
        }

        if (SystemUtils.IS_OS_WINDOWS) {
            CommandLineUtility.runCommand("mklink /J " + nodeModulesLinkPath + " " + nodeModulesPath);
        } else if (SystemUtils.IS_OS_LINUX || SystemUtils.IS_OS_MAC_OSX) {
            Path target = Paths.get(nodeModulesPath);
            Path link = Paths.get(nodeModulesLinkPath);
            try {
                Files.createSymbolicLink(link, target);
            } catch (IOException e) {
                WebLogger.Instance.error(e);
            }
        }

    }


    /**
     * 检测node_modules 是否进行部署
     *
     * @param isRuntime
     */
    private static void checkNodeModuleDeployed(boolean isRuntime) {
        // 获取待执行安装的node_modules 路径
        NpmInstallParameter npmInstallParameter = new NpmInstallParameter();
        // 获取待执行安装的node_modules 路径
        String currentWorkPath = FileUtility.getCurrentWorkPath(false);

        npmInstallParameter.setExecuteEnvironment(isRuntime ? ExecuteEnvironment.Runtime : ExecuteEnvironment.Design);

        String parentNode_ModulesPath = NodeModulesPathGenerator.generatePackageJsonPath(npmInstallParameter, currentWorkPath);

        String currentNodeModulesPath = FileUtility.combine(parentNode_ModulesPath, "node_modules");
        if (!FileUtility.exists(currentNodeModulesPath)) {
            String errorMessage = ResourceLocalizeUtil.getString(I18nMsgConstant.WEB_JIT_ENGINE_MSG_0002, currentNodeModulesPath);
            throw new WebCustomException(errorMessage);
        }
    }

    /**
     * 检测是否包含移动编译必须的npm包
     *
     * @param currentBuildPath
     */
    private static void checkHasMobileRelyNodeModule(String currentBuildPath) {
        String node_modulesPath = FileUtility.combine(currentBuildPath, "node_modules");
        File node_modulesFile = new File(node_modulesPath);
        if (node_modulesFile.exists()) {
            // 获取移动对应的必须包
            File mobileSpecialCli = new File(FileUtility.combine(node_modulesPath, "@farris", "mobile-cli"));
            if (!mobileSpecialCli.exists()) {
                // 如果移动npm包不存在  那么进行提示
                String tipInfo = ResourceLocalizeUtil.getString(I18nMsgConstant.WEB_JIT_ENGINE_MSG_0003);
                WebLogger.Instance.info(tipInfo, JITEngineManager.class.getName());
                throw new WebCustomException(tipInfo);
            }
        }
    }


    /**
     * 不同类型代码存放路径
     *
     * @param projectPath
     * @param terminalType
     * @return
     */
    private static String getProjectBuildPath(String projectPath, TerminalType terminalType) {
        String projectBuildPath = "";
        switch (terminalType) {
            case PC:
                projectBuildPath = FileUtility.combine(projectPath, FrontendProjectConstant.FRONTEND_PROJECT_COMPILE_PATH, TerminalType.PC.getAppPathName());
                break;
            case MOBILE:
                projectBuildPath = FileUtility.combine(projectPath, FrontendProjectConstant.FRONTEND_PROJECT_COMPILE_PATH, TerminalType.MOBILE.getAppPathName());
                break;
            default:
                throw new WebCustomException(I18nExceptionConstant.WEB_JIT_ENGINE_ERROR_0001, new String[]{String.valueOf(terminalType)});
        }

        return projectBuildPath;
    }

    public static void buildFrontendProjectForBabel(String targetProjectPath) {
        if (StringUtility.isNullOrEmpty(targetProjectPath)) {
            return;
        }

        // 转换路径
        targetProjectPath = FileUtility.getPlatformIndependentPath(targetProjectPath);

        // 获取build目录
        String currentBuildPath = targetProjectPath + FileUtility.getPlatformIndependentPath("/src" + "/" + FrontendProjectConstant.PROJECT_GENERATE_PATH_FOR_BABEL);

        // 获取buildfast目录
        String serverInstanceRootPath = FileUtility.getCurrentWorkPath(false);

        String serverInstanceNewRootPath = FileUtility.combine(serverInstanceRootPath, "web");
        String buildFastPath = serverInstanceNewRootPath + FileUtility.getPlatformIndependentPath("/runtime/buildfast");

        String[] command = new String[1];

        StringBuilder sb = new StringBuilder("node @farris/farris-babel/app.js --paths=");
        // 执行node命令的目录，/web/runtime/buildfast
        sb.append(buildFastPath);
        sb.append(" --workspacepath=");
        // app所在目录
        sb.append(currentBuildPath);
        if (SystemUtils.IS_OS_WINDOWS) {
            command = new String[4];
            // buildfast目录在运行环境所在盘符，而不是projects文件所在盘符。
            command[0] = FileUtility.getAbsolutePathHead(serverInstanceRootPath);
            command[1] = "cd " + buildFastPath;
            command[2] = sb.toString();
            command[3] = "exit";
        } else if (SystemUtils.IS_OS_LINUX || SystemUtils.IS_OS_MAC_OSX) {
            command = new String[3];
            // buildfast目录在运行环境所在盘符，而不是projects文件所在盘符。
            command[0] = "cd " + buildFastPath;
            command[1] = sb.toString();
            command[2] = "exit";
        }

        try {
            CommandLineUtility.runCommand(command);
        } catch (WebCustomException e) {
            String errorMsg = ResourceLocalizeUtil.getString(I18nMsgConstant.WEB_JIT_ENGINE_MSG_0004, "babel", e.getMessage());
            throw new WebCustomException(errorMsg, e);
        }
    }

    /**
     * 获取运行时编译路径
     *
     * @return
     */
    private static String getRuntimeBuildRoot(boolean isUpdradeTool) {
        String _localServerPath = FileUtility.getPlatformIndependentPath(FileUtility.getCurrentWorkPath(isUpdradeTool));
        return _localServerPath + "/web/runtime/buildfast";
    }

    /**
     * 获取运行时编译路径
     *
     * @return
     */
    public static boolean getRuntimeWebBuildNoah(boolean isUpdradeTool) {
        String _localServerPath = FileUtility.getPlatformIndependentPath(FileUtility.getCurrentWorkPath(isUpdradeTool));
        String noahJsonPath = _localServerPath + "/web/runtime/noah.json";
        return FileUtility.exists(noahJsonPath);
    }
}
